package com.example.kotlin

///////////////////////////////////////////////////////////////////////////
// 标题：Compose 中的附带效应
// 作者：google
// 地址：https://developer.android.google.cn/jetpack/compose/side-effects?hl=zh-cn
///////////////////////////////////////////////////////////////////////////

/*

附带效应是指发生在可组合函数作用域之外的应用状态的变化。由于可组合项的生命周期和属性（例如不可预测的重组、以不同顺序执行可组合项的重组或可以舍弃的重组），可组合项在理想情况下应该是无附带效应的。
不过，有时附带效应是必要的，例如，触发一次性事件（例如显示信息提示控件），或在满足特定状态条件时进入另一个屏幕。这些操作应从能感知可组合项生命周期的受控环境中调用。
在本页中，您将了解 Jetpack Compose 提供的不同附带效应 API。

如 Compose 编程思想文档中所述，可组合项应该没有附带效应。如果您需要更改应用的状态（如管理状态文档中所述），您应该使用 Effect API，以便以可预测的方式执行这些附带效应。
关键术语：效应是一种可组合函数，该函数不会发出界面，并且在组合完成后不会产生附带效应

由于效应会在 Compose 中带来各种可能性，所以很容易过度使用。确保您在其中完成的工作与界面相关，并且不会破坏单向数据流，如管理状态文档中所述。


*/

/*
LaunchedEffect：在某个可组合项的作用域内运行挂起函数
如需从可组合项内安全调用挂起函数，请使用 LaunchedEffect 可组合项。当 LaunchedEffect 进入组合时，它会启动一个协程，并将代码块作为参数传递。
如果 LaunchedEffect 退出组合，协程将取消。如果使用不同的键重组 LaunchedEffect（请参阅下方的重启效应部分），系统将取消现有协程，并在新的协程中启动新的挂起函数。
@Composable
fun MyScreen(
    state: UiState<List<Movie>>,
    scaffoldState: ScaffoldState = rememberScaffoldState()
) {

    // If the UI state contains an error, show snackbar
    if (state.hasError) {

        // `LaunchedEffect` will cancel and re-launch if
        // `scaffoldState.snackbarHostState` changes
        LaunchedEffect(scaffoldState.snackbarHostState) {
            // Show snackbar using a coroutine, when the coroutine is cancelled the
            // snackbar will automatically dismiss. This coroutine will cancel whenever
            // `state.hasError` is false, and only start when `state.hasError` is true
            // (due to the above if-check), or if `scaffoldState.snackbarHostState` changes.
            scaffoldState.snackbarHostState.showSnackbar(
                message = "Error message",
                actionLabel = "Retry message"
            )
        }
    }

    Scaffold(scaffoldState = scaffoldState) {
        /* ... */
    }
}
*/

/*
rememberCoroutineScope：获取组合感知作用域，以便在可组合项外启动协程
由于 LaunchedEffect 是可组合函数，因此只能在其他可组合函数中使用。为了在可组合项外启动协程，但存在作用域限制，以便协程在退出组合后自动取消，请使用 rememberCoroutineScope。
此外，如果您需要手动控制一个或多个协程的生命周期，请使用 rememberCoroutineScope，例如在用户事件发生时取消动画。
rememberCoroutineScope 是一个可组合函数，会返回一个 CoroutineScope，该 CoroutineScope 绑定到调用它的组合点。调用退出组合后，作用域将取消。
@Composable
fun MoviesScreen(scaffoldState: ScaffoldState = rememberScaffoldState()) {

    // Creates a CoroutineScope bound to the MoviesScreen's lifecycle
    val scope = rememberCoroutineScope()

    Scaffold(scaffoldState = scaffoldState) {
        Column {
            /* ... */
            Button(
                onClick = {
                    // Create a new coroutine in the event handler to show a snackbar
                    scope.launch {
                        scaffoldState.snackbarHostState.showSnackbar("Something happened!")
                    }
                }
            ) {
                Text("Press me")
            }
        }
    }
}
*/

/*
rememberUpdatedState：在效应中引用某个值，该效应在值改变时不应重启
当其中一个键参数发生变化时，LaunchedEffect 会重启。不过，在某些情况下，您可能希望在效应中捕获某个值，但如果该值发生变化，您不希望效应重启。
为此，需要使用 rememberUpdatedState 来创建对可捕获和更新的该值的引用。这种方法对于包含长期操作的效应十分有用，因为重新创建和重启这些操作可能代价高昂或令人望而却步。

例如，假设您的应用的 LandingScreen 在一段时间后消失。即使 LandingScreen 已重组，等待一段时间并发出时间已过通知的效应也不应该重启。
@Composable
fun LandingScreen(onTimeout: () -> Unit) {

    // This will always refer to the latest onTimeout function that
    // LandingScreen was recomposed with
    val currentOnTimeout by rememberUpdatedState(onTimeout)

    // Create an effect that matches the lifecycle of LandingScreen.
    // If LandingScreen recomposes, the delay shouldn't start again.
    LaunchedEffect(true) {
        delay(SplashWaitTimeMillis)
        currentOnTimeout()
    }

    /* Landing screen content */
}

为创建与调用点的生命周期相匹配的效应，永不发生变化的常量（如 Unit 或 true）将作为参数传递。在以上代码中，使用 LaunchedEffect(true)。
为了确保 onTimeout lambda 始终包含重组 LandingScreen 时使用的最新值，onTimeout 需使用 rememberUpdatedState 函数封装。效应中应使用代码中返回的 State、currentOnTimeout
 */

/*
* DisposableEffect：需要清理的效应
对于需要在键发生变化或可组合项退出组合后进行清理的附带效应，请使用 DisposableEffect。如果 DisposableEffect 键发生变化，可组合项需要处理（执行清理操作）其当前效应，并通过再次调用效应进行重置。
* */

/*
* DisposableEffect：需要清理的效应
对于需要在键发生变化或可组合项退出组合后进行清理的附带效应，请使用 DisposableEffect。如果 DisposableEffect 键发生变化，可组合项需要处理（执行清理操作）其当前效应，并通过再次调用效应进行重置。
* */